//=============================================================================
// Project  : NIPO Component Framework
// File    : BranchMergePlugin.cs
// Author  : Bernhard Dieber (Bernhard.Dieber@uni-klu.ac.at)
// Copyright 2010 by Bernhard Dieber
// This code is published under the Microsoft Public License (Ms-PL).  A copy
// of the license should be distributed with the code.  It can also be found
// at the project website: http://NIPO.CodePlex.com.   This notice, the
// author's name, and all copyright notices must remain intact in all
// applications, documentation, and source files.
//=============================================================================

using System;
using System.Collections.Generic;
using NIPO.Data;
using NIPO.Util;

namespace NIPO.Plugins.Extension
{
    /// <summary>
    /// Abstract base class for plugins that have multiple parallel plugins and need to branch on each incoming packet<br />
    /// Each parallel plugin can be a single <see cref="ProcessingPlugin"/> or a <see cref="ProcessingChainPlugin"/>
    /// </summary>
    public abstract class BranchMergePlugin : ProcessingPlugin
    {
        /// <summary>
        /// The list of parallel plugins available for branch
        /// </summary>
        private SortedList<int, ProcessingPlugin> _parallelPlugins = new SortedList<int, ProcessingPlugin>();

        /// <summary>
        /// Gets the parallel plugins.
        /// </summary>
        /// <value>The parallel plugins.</value>
        protected SortedList<int, ProcessingPlugin> ParallelPlugins
        {
            get { return _parallelPlugins; }
        }

        #region Overrides of Plugin

        /// <summary>
        /// The ConfigurationObject is used to have a user configure a plugin.<br />
        /// Provide here a subclass of PluginConfiguration that defines the configuration values of your need
        /// </summary>
        /// <value>The configuration object.</value>
        public override PluginConfiguration ConfigurationObject
        {
            get { return null; }
            set { }
        }

        /// <summary>
        /// Gets a value indicating whether this instance is configured.<br />
        /// "Configured" means that the plugin is ready to run and does not need any additional configuration information
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is configured; otherwise, <c>false</c>.
        /// </value>
        public override bool IsConfigured
        {
            get { return true; }
        }

        /// <summary>
        /// Opportunity for a plugin to do pre-start initialization (e.g. resource allocation, event registration, ...)<br/>
        /// This method is called immediately before the framework start (after checking that IsConfigured == <c>true</c>
        /// </summary>
        /// <remarks>Registration of non-standard events is supposed to happen here. If your plugin is supposed to catch events other than normal data events, this should be done here.</remarks>
        protected internal override void Initialize()
        {
            foreach (KeyValuePair<int, ProcessingPlugin> chain in ParallelPlugins)
            {
                chain.Value.Initialize();
                chain.Value.NewData += new NIPOEventHandler(Merge);
            }
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public override void Dispose()
        {
            ParallelPlugins.Clear();
        }

        /// <summary>
        /// Resets a PlugIn after a framework run. After this call the plugin must be runnable again
        /// </summary>
        public override void Reset()
        {
            foreach (KeyValuePair<int, ProcessingPlugin> chain in ParallelPlugins)
            {
                chain.Value.Reset();
            }
        }

        #endregion

        #region Overrides of ProcessingPlugin

        /// <summary>
        /// This function receives data from every InputPlugin for further processing
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="NIPOEventArgs"/> instance containing the event data.</param>
        public override void ProcessData(object sender, NIPOEventArgs e)
        {
            int branch = Branch(ref e);
            if (!ParallelPlugins.ContainsKey(branch))
            {
                Log.WriteWarningFormat("Could not get valid index ({1} for {0}.", e.Data, branch);
                return;
            }
            ParallelPlugins[branch].ProcessData(sender, e);
        }

        #endregion



        /// <summary>
        /// The merge method. Takes the output of a certain chain, and performs merge logic.
        /// <br />At the end of this Method <see cref="ProcessingPlugin.RaiseNewData"/> should be called to pass the result on to the next Plugin
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="NIPO.Data.NIPOEventArgs"/> instance containing the event data.</param>
        protected abstract void Merge(object sender, NIPOEventArgs e);

        /// <summary>
        /// Branche method. Returns the index of the Plugin in the Parallel Plugins that is chosen to process <paramref name="e"/>.
        /// </summary>
        /// <param name="e">The <see cref="NIPO.Data.NIPOEventArgs"/> instance containing the event data.</param>
        /// <returns>Index ot the plugin to start with <paramref name="e"/></returns>
        protected abstract int Branch(ref NIPOEventArgs e);
    }
}